/*
 * Copyright (c) 2013 by Chances.
 * $CVSHeader$
 * $Author$
 * $Date$
 * $Revision$
 */
package chances.epg.search.searcher;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FloatField;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.RAMDirectory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import chances.epg.api.tools.ContentTool;
import chances.epg.commons.log.EpgLogFactory;
import chances.epg.commons.utils.JSONUtils;
import chances.epg.data.PageBean;
import chances.epg.data.service.ContentService;
import chances.epg.entity.content.EpgContent;
import chances.epg.search.AnalyzerFactory;
import chances.epg.search.IndexField;
import chances.epg.search.LucenceVersion;


/**
 * 建立索引.
 *
 * @author <a href="mailto:libf@chances.com.cn">libf</a>
 */
@Service
@EnableScheduling
public class SearchService {
    private static final Logger logger = EpgLogFactory.getSearchLogger();
    private static ContentTool contentTool = new ContentTool();
    
    @Autowired
    private ContentService contentService;
    
    @Value("${epg.search.index.dir}")
    private Resource indexDir;
    private SearchHelper searchHelper;
    
    public void createSearch() {
    	logger.info("create searcher...");
    	
    	 try{
    		 File fsDir = indexDir.getFile();
    		 Directory ramDir = new RAMDirectory(FSDirectory.open(fsDir),new IOContext());
	         IndexReader reader = DirectoryReader.open(ramDir);
	         IndexSearcher searcher = new IndexSearcher(reader);
	         SearchHelper searchHelper = new SearchHelper(searcher);
	         SearchHelper old = this.searchHelper;
	         this.searchHelper = searchHelper;
	         if(old != null) {
	        	 old.close();
	         }
    	 }catch(Exception ex) {
    		 logger.error(ex.getMessage(),ex);
    	 }
    	 
    	 logger.info("end create searcher.");
    }
    
    public SearchHelper getSearchHelper() {
    	return this.searchHelper;
    }
    
    @PostConstruct
    @Scheduled(cron="0 */5 * * * ?")
    public void createIndex() {
        logger.info("Indexing contents...");
        long beginTime = System.currentTimeMillis();
        IndexWriter writer = null;
        try{
            writer = this.getIndexWriter(true);
            this.indexAllContents(writer);
            writer.commit();
        }catch(IOException ex){
            logger.error("Index all program and series error.",ex);
        }finally{
            this.safeCloseIndexWriter(writer);
        }
        
        logger.info("Index content finish,spend time:" + (System.currentTimeMillis() - beginTime));
        
        //索引创建完成后，重新创建searcher
        this.createSearch();
    }
    
    
    private void indexAllContents(IndexWriter writer) throws IOException{
        logger.info("Indexing all program.");
        long size = 0;
        PageBean pageBean = new PageBean();
        pageBean.setCurrentPage(1);
        pageBean.setPageSize(1000);
        List<EpgContent> epgContents = this.contentService.getAllEpgContents(pageBean);
        
        this.indexContents(writer, epgContents,true);
        size += epgContents.size();
        
        if(pageBean.getPageCount() > 1){
            for (int i = 2; i <= pageBean.getPageCount(); i++) {
                pageBean.setCurrentPage(i);
                epgContents = this.contentService.getAllEpgContents(pageBean);
                this.indexContents(writer, epgContents,true);
                size += epgContents.size();
            }
        }
        logger.info("Indexed all program,size:" + size);
    }
    
    private void indexContents(IndexWriter writer,List<EpgContent> contentList,boolean create) throws IOException {
        if(contentList.size() <= 0){
            return;
        }
        
        for (EpgContent content : contentList) {
            Document doc = new Document();
            doc.add(new LongField(IndexField.FIELD_ID,content.getContentId(),Field.Store.NO));
            doc.add(new FloatField(IndexField.FIELD_SCORE,content.getScoreFloat(),Field.Store.NO));
            doc.add(new StringField(IndexField.FIELD_CODE,content.getContentCode(),Field.Store.YES));
            doc.add(new StringField(IndexField.FIELD_TYPE, content.getType(), Field.Store.YES));
            
            //基础类型
            String baseType = contentTool.typeName(content.getContentBaseType());
            if(StringUtils.isNotBlank(baseType)){
                doc.add(new StringField(IndexField.FIELD_BASE_TYPE,baseType,Field.Store.YES));
            }
            
            //基础标签
            String baseTags = contentTool.baseTagNames(content.getContentBaseTags());
            if(StringUtils.isNotBlank(baseTags)){
            	doc.add(new TextField(IndexField.FIELD_BASE_TAGS,baseTags,Field.Store.YES));
            }
            
            //运营标签
            String opTags = contentTool.opTagNames(content.getContentOpTags());
            if(StringUtils.isNotBlank(opTags)){
            	doc.add(new TextField(IndexField.FIELD_OP_TAGS,opTags,Field.Store.YES));
            }
            
            //演员
            if(StringUtils.isNotBlank(content.getActors())){
            	doc.add(new TextField(IndexField.FIELD_ACTOR,content.getActors(),Field.Store.YES));
            }
            
            //导演
            if(StringUtils.isNotBlank(content.getDirector())){
            	doc.add(new TextField(IndexField.FIELD_DIRECTOR,content.getDirector(),Field.Store.YES));
            }
            
            //主持人
            if(StringUtils.isNotBlank(content.getCompere())){
            	doc.add(new TextField(IndexField.FIELD_COMPERE,content.getCompere(),Field.Store.YES));
            }
            
            //地区
            if(StringUtils.isNotBlank(content.getCountry())){
            	doc.add(new StringField(IndexField.FIELD_REGION,content.getCountry(),Field.Store.YES));
            }
            
            //年份
            if(StringUtils.isNotBlank(content.getYear())){
            	doc.add(new StringField(IndexField.FIELD_YEAR,content.getSearchYear(),Field.Store.YES));
            }
            
            //高标清
            if(content.getHdType() != null) {
                doc.add(new StringField(IndexField.FIELD_HDTYPE,content.getHdType() + "",Field.Store.YES));
            }
            
            doc.add(new StoredField(IndexField.FIELD_CONTENT,JSONUtils.fromObject(content)));
            
            if(writer.getConfig().getOpenMode() == OpenMode.CREATE){
            	writer.addDocument(doc);
            }else{
            	Term ter = new Term(IndexField.FIELD_CODE,content.getContentCode());
                writer.updateDocument(ter, doc);	
            }
        }
    }
    
    private IndexWriter getIndexWriter(boolean create) throws IOException {
    	
        Directory dir = FSDirectory.open(this.indexDir.getFile());
        Analyzer defaultAnalyzer = AnalyzerFactory.getDefaultAnalyzer();
        IndexWriterConfig iwc = new IndexWriterConfig(LucenceVersion.VERSION, defaultAnalyzer);

        if (create) {
          iwc.setOpenMode(OpenMode.CREATE);
        } else {
          iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
        }
        
        IndexWriter writer = new IndexWriter(dir, iwc);
        return writer;
    }
    
    private void safeCloseIndexWriter(IndexWriter writer) {
        if(writer != null){
            try {
                writer.close();
            } catch (IOException ex) {
                logger.error("Close index writer error.",ex);
            }
        }
    }

  
  
}
